// DiplomacyHelper.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;

using Supremacy.Annotations;
using Supremacy.Buildings;
using Supremacy.Collections;
using Supremacy.Data;
using Supremacy.Economy;
using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.Orbitals;
using Supremacy.Types;
using Supremacy.Universe;
using Supremacy.Utility;

namespace Supremacy.Diplomacy
{
    public class DiplomacyHelper
    {
        public static IList<Civilization> GetAllies(Civilization who)
        {
            return (from civ in GameContext.Current.Civilizations
                    let relationship = GameContext.Current.Relationships[who, civ]
                    where ((civ != who) && relationship.IsContactMade && relationship.Allied)
                    select civ).ToList();
        }

        public static IList<Civilization> GetMemberCivilizations(Civilization who)
        {
            return (from civ in GameContext.Current.Civilizations
                    let relationship = GameContext.Current.Relationships[who, civ]
                    where ((civ != who) && relationship.IsContactMade && relationship.IsMember)
                    select civ).ToList();
        }

        public static Civilization GetWorstEnemy(Civilization who)
        {
            return GameContext.Current.Relationships.GetRelationshipsForCiv(who)
                .MinElement(o => o.Attitude.CurrentValue)
                .OtherCiv;
        }

        public static bool IsSafeTravelGuaranteed(Civilization traveller, Civilization territoryOwner)
        {
            if (traveller == null)
                throw new ArgumentNullException("traveller");
            return (territoryOwner == null) || GameContext.Current.Relationships[traveller, territoryOwner].IsFriendly;
        }

        public static bool AreAllied(Civilization who, Civilization whoElse)
        {
            if (who == null)
                throw new ArgumentNullException("who");
            if (whoElse == null)
                throw new ArgumentNullException("whoElse");

            var relationship = GameContext.Current.Relationships[who, whoElse];

            return ((relationship != null) && relationship.Allied);
        }

        public static bool AreAtWar(Civilization who, Civilization whoElse)
        {
            if (who == null)
                throw new ArgumentNullException("who");
            if (whoElse == null)
                throw new ArgumentNullException("whoElse");

            var relationship = GameContext.Current.Relationships[who, whoElse];

            return ((relationship != null) && relationship.AtWar);
        }

        public static bool AreNeutral(Civilization who, Civilization whoElse)
        {
            if (who == null)
                throw new ArgumentNullException("who");
            if (whoElse == null)
                throw new ArgumentNullException("whoElse");

            var relationship = GameContext.Current.Relationships[who, whoElse];

            return (relationship != null &&
                    relationship.Status == RelationshipStatus.Neutral);
        }

        public static bool AreEnemies(Civilization who, Civilization whoElse)
        {
            return AreAtWar(who, whoElse);
        }

        public static bool IsMember(Civilization minorPower, Civilization empire)
        {
            if (minorPower == null)
                throw new ArgumentNullException("minorPower");
            if (empire == null)
                throw new ArgumentNullException("empire");

            if (minorPower.IsEmpire || !empire.IsEmpire)
                return false;

            var relationship = GameContext.Current.Relationships[minorPower, empire];

            return ((relationship != null) && relationship.IsMember);
        }

        public static bool IsAlliedWithWorstEnemy(Civilization us, Civilization them)
        {
            if (us == null)
                throw new ArgumentNullException("us");
            if (them == null)
                throw new ArgumentNullException("them");
            
            Civilization worstEnemy = GetWorstEnemy(us);
            
            if (worstEnemy == null)
                return false;

            int worstEnemyAttitude = GameContext.Current.Relationships[us, worstEnemy].Attitude.CurrentValue;

            return GameContext.Current.Relationships.GetRelationshipsForCiv(them)
                .Where(o => o.OtherCiv != us)
                .Any(o => o.Allied && ((o.OtherCiv == worstEnemy) || (o.Attitude.CurrentValue <= worstEnemyAttitude))); 
        }

        public static int GetResourceCreditValue(ResourceType resource)
        {
            switch (resource)
            {
                case ResourceType.Deuterium:
                    return 50;
                case ResourceType.Dilithium:
                    return 150;
                case ResourceType.RawMaterials:
                    return 35;
                default:
                    return 0;
            }
        }

        public static double GetAttitudeVariable(Civilization civ, AttitudeVariable variable)
        {
            return 0.0;
        }

        private static int ComputeValue(Civilization civ, ResourceValueCollection resources)
        {
            double totalValue = 0.0;
            CivilizationManager civManager = GameContext.Current.CivilizationManagers[civ];
            
            Table baseValues = GameContext.Current.Tables.ResourceTables["BaseCreditValues"];
            Table modifiers = GameContext.Current.Tables.ResourceTables["DiminishedUtilityModifiers"];

            foreach (ResourceType resource in Enum.GetValues(typeof(ResourceType)))
            {
                if (resources[resource] <= 0)
                    continue;

                double productionRatio = 0.0;
                double unitValue = Number.ParseDouble(baseValues[resource.ToString()][0]);

                if (civManager != null)
                {
                    double modifier = 1.0;

                    if (civManager.Resources[resource].CurrentValue != 0)
                    {
                        productionRatio = (double)civManager.Resources[resource].LastChange
                            / civManager.Resources[resource].CurrentValue;

                        if (productionRatio < -1.0)
                            productionRatio = -1.0;
                        else if (productionRatio > 1.0)
                            productionRatio = 1.0;
                    }

                    for (int i = 0; i < modifiers.Rows.Count; i++)
                    {
                        double testModifier = Number.ParseDouble(modifiers[i][0].Trim());
                        if ((productionRatio < testModifier)
                            || ((i == (modifiers.Rows.Count - 1)) && (productionRatio >= testModifier)))
                        {
                            modifier = testModifier;
                        }
                    }

                    unitValue *= modifier;
                }

                totalValue += (unitValue * resources[resource]);
            }

            return (int)Math.Floor(totalValue);
        }

        public static void EnsureContact([NotNull] Civilization firstCiv, [NotNull] Civilization secondCiv, MapLocation location)
        {
            if (firstCiv == null)
                throw new ArgumentNullException("firstCiv");
            if (secondCiv == null)
                throw new ArgumentNullException("secondCiv");
            if (firstCiv == secondCiv)
                return;
            Relationship relationship = GameContext.Current.Relationships[firstCiv, secondCiv];
            if (!relationship.IsContactMade)
            {
                CivilizationManager firstManager = GameContext.Current.CivilizationManagers[firstCiv];
                CivilizationManager secondManager = GameContext.Current.CivilizationManagers[secondCiv];
                
                relationship.MakeContact();

                if (firstManager != null)
                {
                    firstManager.SitRepEntries.Add(
                        new FirstContactSitRepEntry(firstCiv, secondCiv, location));
                }

                if (secondManager != null)
                {
                    secondManager.SitRepEntries.Add(
                        new FirstContactSitRepEntry(secondCiv, firstCiv, location));
                }
            }
        }

        internal static void PerformFirstContacts(Civilization civilization, MapLocation location)
        {
            List<Civilization> otherCivs = new List<Civilization>();
            var populationSites = from UniverseObject item in GameContext.Current.Universe.Objects 
                                  where (item.Location == location)
                                        && (item.OwnerID != civilization.CivID)
                                        && ((item is Orbital) || (item is Colony))
                                  select item;
            foreach (UniverseObject item in populationSites)
            {
                if (!otherCivs.Contains(item.Owner))
                    otherCivs.Add(item.Owner);
            }
            foreach (Civilization otherCiv in otherCivs)
            {
                EnsureContact(civilization, otherCiv, location);
            }
        }

        public static bool IsContactMade(Civilization source, Civilization target)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (target == null)
                throw new ArgumentNullException("target");
            if (source == target)
                return true;
            return (GameContext.Current.DiplomacyStatus[source, target] != DiplomacyStatus.NoContact);
        }

        public static bool IsFirstContact(Civilization source, Civilization target)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (target == null)
                throw new ArgumentNullException("target");
            if (source == target)
                return false;
            Relationship relationship = GameContext.Current.Relationships[source, target];
            return (relationship.ContactDuration == 0);
        }

        public static int ComputeResourceValue(ResourceType resourceType, int p)
        {
            return p;
        }

        public static int ComputeColonyValue(Colony colony)
        {
            if (colony == null)
                return 0;

            int totalValue = 0;

            foreach (Building building in colony.Buildings)
            {
                totalValue += building.Design.BuildCost;
                foreach (var resource in EnumHelper.GetValues<ResourceType>())
                {
                    totalValue += ComputeResourceValue(
                        resource,
                        building.Design.BuildResourceCosts[resource]);
                }
            }

            var shipyard = colony.Shipyard;
            if (shipyard != null)
            {
                totalValue += shipyard.Design.BuildCost;
                foreach (var resource in EnumHelper.GetValues<ResourceType>())
                {
                    totalValue += ComputeResourceValue(
                        resource,
                        shipyard.Design.BuildResourceCosts[resource]);
                }
            }

            foreach (var productionCategory in EnumHelper.GetValues<ProductionCategory>())
            {
                int facilityCount = colony.GetTotalFacilities(productionCategory);
                if (facilityCount == 0)
                    continue;
                var facilityType = colony.GetFacilityType(productionCategory);
                if (facilityType == null)
                    continue;
                totalValue += facilityType.BuildCost;
                foreach (var resource in EnumHelper.GetValues<ResourceType>())
                {
                    totalValue += ComputeResourceValue(
                        resource,
                        facilityType.BuildResourceCosts[resource]);
                }
            }

            return totalValue;
        }

        public static int ComputeEndWarValue(Civilization sender, Civilization recipient)
        {
            if (sender == null)
                throw new ArgumentNullException("sender");
            if (recipient == null)
                throw new ArgumentNullException("recipient");

            return 0;
        }

        public static bool IsCreditsDeal(IEnumerable<TradeData> items)
        {
            if (items == null)
                return false;
            foreach (TradeData item in items)
            {
                switch (item.ItemType)
                {
                    case TradableItem.Credits:
                    case TradableItem.CreditsPerTurn:
                        return true;
                }
            }
            return false;
        }

        public static int GetInitialMemoryWeight(Civilization civ, MemoryType memoryType)
        {
            int weight = 0;
            try
            {
                Table table = GameContext.Current.Tables.DiplomacyTables["RelationshipMemoryWeights"];
                if (!int.TryParse(table[memoryType.ToString()][0], out weight))
                {
                    weight = 0;
                }
            }
            catch {}
            return weight;
        }
    }
}
